package com.aiwiown.snackmq.console.auth;

import com.aiwiown.snackmq.api.exception.SnackMQClientException;
import com.aiwiown.snackmq.common.auth.AuthenticatedUser;
import com.aiwiown.snackmq.console.service.SnackMQAdminService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 自定义的 Spring Security 认证提供者。
 * 它将认证逻辑委托给 SnackMQ Broker。
 */
@Slf4j
@Component
public class SnackMQAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private SnackMQAdminService adminService;
    @Autowired
    private HttpSession httpSession; // 自动注入当前的 HTTP Session

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        try {
            // 调用我们自己的服务，向 Broker 发送认证请求
            AuthenticatedUser user = adminService.login(username, password);
            // 【修复】增加对认证返回结果的空指针检查
            if (user == null || user.getToken() == null || user.getRoles() == null) {
                log.error("Authentication failed for user '{}'. Broker returned incomplete user details.", username);
                throw new BadCredentialsException("Authentication failed: received no or incomplete user details from broker.");
            }
            // 登录成功后，将用户的 Token 存储在 Session 中，供后续请求使用
            httpSession.setAttribute("user_token", user.getToken());

            // 根据 Broker 返回的角色，创建 Spring Security 的权限列表
            List<GrantedAuthority> authorities = user.getRoles().stream()
                    .map(role -> new SimpleGrantedAuthority("ROLE_" + role)) // "ROLE_" 前缀是 Spring Security 的约定
                    .collect(Collectors.toList());

            // 返回一个已认证的、包含权限的 Authentication 对象
            return new UsernamePasswordAuthenticationToken(username, null, authorities);
        } catch (SnackMQClientException e) {
            // 如果 Broker 认证失败，则抛出 Spring Security 的标准异常
            throw new BadCredentialsException("Invalid username or password provided.", e);
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        // 表明此 Provider 只处理 UsernamePasswordAuthenticationToken 类型的认证请求
        return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);
    }
}